package schedulo.gui;

import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.io.IOException;
import java.util.AbstractSequentialList;
import java.util.EventObject;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import javax.swing.AbstractCellEditor;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumnModel;

import schedulo.core.task.Task;
import schedulo.core.task.reminder.Reminder;
import schedulo.core.task.reminder.ReminderListener;

public class ReminderGui {

	private ReminderFrame reminderFrame;

	private JTable reminderTable = null;

	private ReminderTableModel reminderTableModel = null;

	class ReminderFrame extends JFrame {

		private static final long serialVersionUID = 5338226833262653380L;

		public ReminderFrame() {
			super("Schedulo - Task Reminder");
			initialize();
		}

		private void initialize() {

			// reminded task list
			reminderTableModel = new ReminderTableModel();
			reminderTable = new JTable(reminderTableModel);
			reminderTable.setTableHeader(null);
			ReminderTaskEditorRenderer editorrenderer = new ReminderTaskEditorRenderer();
			reminderTable.setDefaultRenderer(Task.class, editorrenderer);
			reminderTable.setDefaultRenderer(Object.class, editorrenderer);
			reminderTable.setDefaultEditor(Object.class, editorrenderer);
			reminderTable.setCellEditor(editorrenderer);

			// new JScrollPane
			JScrollPane scrollPane = new JScrollPane(reminderTable);
			scrollPane
					.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);

			setContentPane(scrollPane);
		}
	}

	class ReminderTaskEditorRenderer extends AbstractCellEditor implements
			TableCellRenderer, TableCellEditor {

		private static final long serialVersionUID = 4202691082879107215L;

		class ReminderTaskPanel extends JPanel {

			private static final long serialVersionUID = 7929051932934032182L;

			private Task task;

			private JTextArea taskDesc;

			private JScrollPane taskDescScrollPane;

			private JButton snooze, done;

			public ReminderTaskPanel(Task taskForPanel) {
				this.task = taskForPanel;
				setLayout(new GridBagLayout());
				GridBagConstraints gbc = new GridBagConstraints();
				gbc.fill = GridBagConstraints.BOTH;
				gbc.gridx = 0;
				gbc.gridy = 0;
				gbc.weightx = 1.0;
				gbc.weighty = 1.0;
				gbc.gridheight = 3;
				gbc.insets = new Insets(5, 5, 5, 0);
				taskDesc = new JTextArea();
				taskDesc.setWrapStyleWord(true);
				taskDesc.setEditable(false);
				taskDesc.setLineWrap(true);
				taskDesc.setOpaque(false);
				taskDesc.setText(taskForPanel.toString());
				taskDescScrollPane = new JScrollPane(taskDesc);
				taskDescScrollPane.setBorder(null);
				add(taskDescScrollPane, gbc);

				snooze = new JButton("Snooze");
				snooze.addActionListener(new ActionListener() {
					public void actionPerformed(ActionEvent e) {
						reminderTableModel.snooze(task, 10);
					}
				});
				gbc.fill = GridBagConstraints.BOTH;
				gbc.gridx = 1;
				gbc.gridy = 0;
				gbc.weightx = 0;
				gbc.weighty = 0;
				gbc.gridheight = 1;
				gbc.insets = new Insets(5, 5, 0, 5);
				add(snooze, gbc);

				done = new JButton("Done");
				done.addActionListener(new ActionListener() {
					public void actionPerformed(ActionEvent e) {
						reminderTableModel.done(task);
					}
				});
				gbc.fill = GridBagConstraints.BOTH;
				gbc.gridx = 1;
				gbc.gridy = 1;
				gbc.weightx = 0;
				gbc.weighty = 0;
				gbc.gridheight = 1;
				gbc.insets = new Insets(5, 5, 5, 5);
				add(done, gbc);
			}
		}

		public Component getTableCellEditorComponent(JTable table,
				Object value, boolean isSelected, int row, int column) {
			return getTableCellRendererComponent(table, value, isSelected,
					true, row, column);
		}

		public boolean shouldSelectCell(EventObject anEvent) {
			return true;
		}

		public boolean stopCellEditing() {
			fireEditingStopped();
			return true;
		}

		public Object getCellEditorValue() {
			return null;
		}

		public boolean isCellEditable(EventObject e) {
			return true;
		}

		public Component getTableCellRendererComponent(JTable table,
				Object value, boolean isSelected, boolean hasFocus, int row,
				int column) {
			ReminderTaskPanel component = new ReminderTaskPanel((Task) value);

			Dimension snoozeSize = component.snooze.getPreferredSize();
			component.taskDescScrollPane.setPreferredSize(new Dimension(table.getWidth()
					- 10 - snoozeSize.width, snoozeSize.height * 2 + 15));
			component.taskDesc.setLineWrap(false);
			component.taskDesc.setLineWrap(true);

			int height = component.getPreferredSize().height;
			if (table.getRowHeight(row) != height) {
				table.setRowHeight(row, height);
			}
			return component;
		}
	}

	class ReminderWindowListener extends WindowAdapter {

		public void windowClosing(WindowEvent e) {

			// snooze all tasks on window closed
			reminderTableModel.snoozeAll(10);
		}
	}

	class ReminderTableModel extends AbstractTableModel implements
			ReminderListener {

		private static final long serialVersionUID = -4168738214131715985L;

		private LinkedList<Task> taskReminded;

		public ReminderTableModel() {
			taskReminded = new LinkedList<Task>();

			// register in the reminder
			Reminder.getInstance().addListener(this);
		}

		public void remind(Task taskToRemind) {
			// process task only if not yet displayed
			if (taskReminded.contains(taskToRemind)) {
				return;
			}

			// add the task to the reminder list
			taskReminded.add(taskToRemind);

			showWindow();

			// fire content added
			fireTableStructureChanged();
			fireTableDataChanged();
		}

		public void snooze(Task taskToSnooze, int snoozeDelaySecond) {
			// snooze task in the reminder
			Reminder.getInstance().snoozeTask(taskToSnooze, snoozeDelaySecond);

			taskReminded.remove(taskToSnooze);

			// set the task as not displayed anymore
			fireTableStructureChanged();
			fireTableDataChanged();

			if (taskReminded.size() == 0) {
				hideWindow();
			}
		}

		public void snoozeAll(int snoozeDelaySecond) {
			Iterator<Task> it = taskReminded.iterator();
			while (it.hasNext()) {
				Task task = it.next();

				// snooze task in the reminder
				Reminder.getInstance().snoozeTask(task, snoozeDelaySecond);

				// set the task as not displayed anymore
				it.remove();

				fireTableStructureChanged();
				fireTableDataChanged();

			}

			hideWindow();
		}

		public void done(Task taskDone) {
			// set task done
			taskDone.setStatus(Task.STATUS_DONE);

			// inform provider
			try {
				taskDone.getTaskProvider().save();
			} catch (IOException e) {
				e.printStackTrace();
			}

			// fire removed
			fireTableStructureChanged();
			fireTableDataChanged();

			taskReminded.remove(taskDone);

			if (taskReminded.size() == 0) {
				hideWindow();
			}
		}

		public int getColumnCount() {
			return 1;
		}

		public int getRowCount() {
			return taskReminded.size();
		}

		public Object getValueAt(int rowIndex, int columnIndex) {
			return taskReminded.get(rowIndex);
		}

		public boolean isCellEditable(int rowIndex, int columnIndex) {
			return true;
		}
	}

	private void hideWindow() {
		reminderFrame.setVisible(false);
	}

	private void showWindow() {
		// don't popup as active window if already displayed
		if (!reminderFrame.isVisible()) {
			reminderFrame.setVisible(true);
		}
	}

	public ReminderGui() {
		reminderFrame = new ReminderFrame();
		reminderFrame.addWindowListener(new ReminderWindowListener());
		reminderFrame.setSize(new Dimension(300, 200));
	}
}
